/* ARPとPING */

#include "defines.h"
#include "timer.h"
#include "lib.h"
#include "kozos.h"

#define REGADDR 0x200000
#define CR      ((volatile uint8 *) REGADDR + 0x00)
#define RDMAP   ((volatile uint8 *) REGADDR + 0x10)
#define RP      ((volatile uint8 *) REGADDR + 0x18)

#define PSTART  ((volatile uint8 *) REGADDR + 0x01)
#define PSTOP   ((volatile uint8 *) REGADDR + 0x02)
#define BNRY    ((volatile uint8 *) REGADDR + 0x03)
#define TPSR    ((volatile uint8 *) REGADDR + 0x04)
#define TBCR0   ((volatile uint8 *) REGADDR + 0x05)
#define TBCR1   ((volatile uint8 *) REGADDR + 0x06)
#define ISR     ((volatile uint8 *) REGADDR + 0x07)
#define RSAR0   ((volatile uint8 *) REGADDR + 0x08)
#define RSAR1   ((volatile uint8 *) REGADDR + 0x09)
#define RBCR0   ((volatile uint8 *) REGADDR + 0x0a)
#define RBCR1   ((volatile uint8 *) REGADDR + 0x0b)
#define RCR     ((volatile uint8 *) REGADDR + 0x0c)
#define TCR     ((volatile uint8 *) REGADDR + 0x0d)
#define DCR     ((volatile uint8 *) REGADDR + 0x0e)
#define IMR     ((volatile uint8 *) REGADDR + 0x0f)

#define PAR0    ((volatile uint8 *) REGADDR + 0x01)
#define PAR1    ((volatile uint8 *) REGADDR + 0x02)
#define PAR2    ((volatile uint8 *) REGADDR + 0x03)
#define PAR3    ((volatile uint8 *) REGADDR + 0x04)
#define PAR4    ((volatile uint8 *) REGADDR + 0x05)
#define PAR5    ((volatile uint8 *) REGADDR + 0x06)
#define CURR    ((volatile uint8 *) REGADDR + 0x07)
#define MAR0    ((volatile uint8 *) REGADDR + 0x08)
#define MAR1    ((volatile uint8 *) REGADDR + 0x09)
#define MAR2    ((volatile uint8 *) REGADDR + 0x0a)
#define MAR3    ((volatile uint8 *) REGADDR + 0x0b)
#define MAR4    ((volatile uint8 *) REGADDR + 0x0c)
#define MAR5    ((volatile uint8 *) REGADDR + 0x0d)
#define MAR6    ((volatile uint8 *) REGADDR + 0x0e)
#define MAR7    ((volatile uint8 *) REGADDR + 0x0f)

uint8 src_MAC[6];
uint8 src_IP[4] = {192, 168, 0, 10};
uint8 router_IP[4] = {192, 168, 0, 1};
uint8 pc_IP[4] = {192, 168, 0, 2};

#define BUFFER_SIZE 1500

int rtc8019_init()
{
    int i;
    volatile uint8 x;
    
    x = *RP;
    *RP = x;
    delay_ms(10);
    
    *CR = 0x21;
    *DCR = 0x4a;
    *RBCR0 = 0;
    *RBCR1 = 0;
    *RCR = 0x20;
    *TCR = 0x02;
    *TPSR = 0x40;
    *PSTART = 0x46;
    *BNRY = 0x46;
    *PSTOP = 0x60;
    *IMR = 0;
    *ISR = 0xff;
    
    *RBCR0 = 12;
    *RBCR1 = 0;
    *RSAR0 = 0;
    *RSAR1 = 0;
    *CR = 0x0a;

    for (i = 0; i < 6; i++) {
        src_MAC[i] = *RDMAP;
        x = *RDMAP;
    }
    do {
        x = *ISR;
    } while (!(x & 0x40));

    puts("H8 ");
    for (i = 0; i < 5; i++) {
        putxval(src_MAC[i], 2);
        puts(":");
    }
    putxval(src_MAC[i], 2);
    puts("\n");
    
    *CR = 0x61;
    *PAR0 = src_MAC[0];
    *PAR1 = src_MAC[1];
    *PAR2 = src_MAC[2];
    *PAR3 = src_MAC[3];
    *PAR4 = src_MAC[4];
    *PAR5 = src_MAC[5];
    
    *CURR = 0x47;
    
    *MAR0 = 0;
    *MAR1 = 0;
    *MAR2 = 0;
    *MAR3 = 0;
    *MAR4 = 0;
    *MAR5 = 0;
    *MAR6 = 0;
    *MAR7 = 0;
    
    *CR = 0x21;
    *RCR = 0x04;
    *CR = 0x22;
    *TCR = 0;
    *IMR = 0;
    
    return 0;
}

int packet_send(uint8 *packet, uint16 size)
{
    uint16 i;
    uint8 size_h, size_l, x;
    
    if (size < 60) size = 60;
    
    size_l = (uint8)(size & 0x00ff);
    size_h = (uint8)(size >> 8);
    
    *CR = 0x22;
    *RBCR0 = size_l;
    *RBCR1 = size_h;
    *RSAR0 = 0;
    *RSAR1 = 0x40;
    *CR = 0x12;
    for (i = 0; i < size; i++)
        *RDMAP = packet[i];
    do {
        x = *ISR;
    } while (!(x & 0x40));

    *CR = 0x22;
    *TBCR0 = size_l;
    *TBCR1 = size_h;
    *TPSR = 0x40;
    *CR = 0x26;
    do {
        x = *CR;
    } while (!(x & 0x04));

    return 0;
}

uint8 packet_receive(uint8 *packet)
{
    uint16 i, size;
    uint8 data, size_H, size_L, boundary_page, start_page, current_page;
    uint8 header[4];
    
    *CR = 0x22;
    boundary_page = *BNRY;
    *CR = 0x62;
    current_page = *CURR;
    
    if (current_page < boundary_page) {
        current_page += 0x60 - 0x46;
    }
    if (current_page == boundary_page + 1) {
        return 1;
    }
    
    start_page = boundary_page + 1;
    if (start_page == 0x60) {
        start_page = 0x46;
    }
    
    *CR = 0x22;
    *RBCR0 = 4;
    *RBCR1 = 0;
    *RSAR0 = 0;
    *RSAR1 = start_page;
    *CR = 0x0a;
    for (i = 0; i < 4; i++) {
        header[i] = *RDMAP;
    }
    do {
        data = *ISR;
    } while (!(data & 0x40));
    
    *CR = 0x22;
    size_L = header[2];
    size_H = header[3];
    size = ((uint16)size_H <<8) + (uint16)size_L;
    *RBCR0 = size_L;
    *RBCR1 = size_H;
    *RSAR0 = 0;
    *RSAR1 = start_page;
    *CR = 0x0a;
    for (i = 0; i < 4; i++) {
        data = *RDMAP;
    }
    for (i = 0; i < size-4; i++) {
        packet[i] = *RDMAP;
        if (i >= BUFFER_SIZE) { // バッファに入らない分は読み飛ばす
            data = *RDMAP;
        }
    }
    do {
        data = *ISR;
    } while (!(data & 0x40));

    *CR = 0x22;
    boundary_page = current_page - 1;
    if (boundary_page >= 0x60) {
        boundary_page -= 0x60 - 0x46;
    }
    *BNRY = boundary_page;
    
    return 0;
}

int ip_equal(uint8 *i1, uint8 *i2)
{
    return !memcmp(i1, i2, 4);
}

uint16 ip_checksum(uint8 *data, uint16 size)
{
    uint16 i;
    uint32 sum = 0;
    
    for (i = 0; i < size - 1; i += 2) {
        sum += ((uint32)data[i] << 8) + data[i+1];
        sum = (sum & 0xffff) + (sum >> 16);
    }
    
    if (i == size - 1) { // 奇数データ長の場合
        sum += (uint32)data[i] << 8;
        sum = (sum & 0xffff) + (sum >> 16);    
    }

    return ~(uint16)sum;
}

typedef struct {
    uint8 eth_dst_MAC[6];
    uint8 eth_src_MAC[6];
    uint16 eth_ethernet_type; /* IP: 0x0800, ARP: 0x0806 */
} ethernet_header;

typedef struct {
    uint16 arp_hardware_type; /* Ethernet: 1 */
    uint16 arp_protocol_type; /* IPv4: 0x0800 */
    uint8 arp_hardware_length; // 6
    uint8 arp_protocol_length; // 4
    uint16 arp_operation; // request: 1, reply: 2
    uint8 arp_src_MAC[6];
    uint8 arp_src_IP[4];
    uint8 arp_dst_MAC[6];
    uint8 arp_dst_IP[4];
} arp_packet;

typedef struct {
    uint8 ip_version_length;
    uint8 ip_service_type;
    uint16 ip_total_length;
    uint16 ip_id;
    uint16 ip_flags_fragment_offset;
    uint8 ip_time_to_live;
    uint8 ip_protocol; // TCP: 6, UDP: 0x11, ICMP: 1
    uint16 ip_checksum;
    uint8 ip_src_IP[4];
    uint8 ip_dst_IP[4];
} ip_header;

typedef struct {
    uint8 ping_type; // request: 8, reply: 0
    uint8 ping_code;
    uint16 ping_checksum;
    uint16 ping_id;
    uint16 ping_sequence_number;
} ping_header;

int arp_request(uint8 *buffer, uint8 *dst_IP)
{
    int i;
    ethernet_header *ehdr = (ethernet_header *)buffer;
    arp_packet *apacket = (arp_packet *)(buffer + sizeof(*ehdr));

    for (i = 0; i < 6; i++) {
        ehdr->eth_dst_MAC[i] = 0xff;
        ehdr->eth_src_MAC[i] = src_MAC[i];
    }
    ehdr->eth_ethernet_type = 0x0806;
    
    apacket->arp_hardware_type = 1;
    apacket->arp_protocol_type = 0x0800;
    apacket->arp_hardware_length = 6;
    apacket->arp_protocol_length = 4;
    apacket->arp_operation = 1;
    for (i = 0; i < 6; i++) {
        apacket->arp_src_MAC[i] = src_MAC[i];
        apacket->arp_dst_MAC[i] = 0;
    }
    for (i = 0; i < 4; i++) {
        apacket->arp_src_IP[i] = src_IP[i];
        apacket->arp_dst_IP[i] = dst_IP[i];
    }
    
    packet_send(buffer, 60);
    
    return 0;
}

void arp_reply(uint8 *buffer)
{
    int i;
    ethernet_header *ehdr = (ethernet_header *)buffer;
    arp_packet *apacket = (arp_packet *)(buffer + sizeof(*ehdr));
    
    for (i = 0; i < 6; i++) {
        ehdr->eth_dst_MAC[i] = ehdr->eth_src_MAC[i];
        ehdr->eth_src_MAC[i] = src_MAC[i];
    }
    
    apacket->arp_operation = 2;
    for (i = 0; i < 6; i++) {
        apacket->arp_dst_MAC[i] = apacket->arp_src_MAC[i];
        apacket->arp_src_MAC[i] = src_MAC[i];
    }
    for (i = 0; i < 4; i++) {
        apacket->arp_dst_IP[i] = apacket->arp_src_IP[i];
        apacket->arp_src_IP[i] = src_IP[i];
    }
    
    packet_send(buffer, 60);
}

void ping_reply(uint8 *buffer)
{
    uint16 i;
    ethernet_header *ehdr = (ethernet_header *)buffer;
    ip_header *ihdr = (ip_header *)(buffer + sizeof(*ehdr));
    ping_header *phdr = (ping_header *)((uint8 *)ihdr + sizeof(*ihdr));
    
    for (i = 0; i < 6; i++) {
        ehdr->eth_dst_MAC[i] = ehdr->eth_src_MAC[i];
        ehdr->eth_src_MAC[i] = src_MAC[i];
    }
    for (i = 0; i < 4; i++) {
        ihdr->ip_dst_IP[i] = ihdr->ip_src_IP[i];
        ihdr->ip_src_IP[i] = src_IP[i];
    }
    phdr->ping_type = 0;
    
    ihdr->ip_checksum = 0;
    ihdr->ip_checksum = ip_checksum((uint8 *)ihdr, sizeof(*ihdr));
    
    phdr->ping_checksum = 0;
    phdr->ping_checksum = ip_checksum((uint8 *)phdr, ihdr->ip_total_length - sizeof(*ihdr));
    
    packet_send(buffer, sizeof(*ehdr) + ihdr->ip_total_length);
}

int nictest_main(int argc, char *argv[])
{
    uint16 i;
    uint8 packet_type;
    uint8 dst_MAC[6];
    uint8 *dst_IP;
    uint8 *buffer;
    ethernet_header *ehdr; 
    
    rtc8019_init();

    buffer = (uint8 *)kz_kmalloc(BUFFER_SIZE);
    ehdr = (ethernet_header *)buffer;
    arp_packet *apacket = (arp_packet *)(buffer + sizeof(*ehdr));
    ip_header *ihdr = (ip_header *)(buffer + sizeof(*ehdr));
    ping_header *phdr = (ping_header *)((uint8 *)ihdr + sizeof(*ihdr));
    
    // PCのMACを得る。
    dst_IP = pc_IP;
    arp_request(buffer, dst_IP);    
    
    packet_type = 0;
    while (1) {
       if (packet_receive(buffer) == 0) {
       
            if ((ehdr->eth_ethernet_type == 0x0806) 
                    && ip_equal(apacket->arp_dst_IP, src_IP)
                    && ip_equal(apacket->arp_src_IP, dst_IP)
                    && (apacket->arp_operation == 2))
            {
                memcpy(dst_MAC, apacket->arp_src_MAC, 6);
                break;
            }
            
        }
    }
    
    puts("PC ");
    for (i = 0; i < 5; i++) {
        putxval(dst_MAC[i], 2);
        puts(":");
    }
    putxval(dst_MAC[i], 2);
    puts("\n");

    // ルータのMACを得る
    dst_IP = router_IP;
    arp_request(buffer, dst_IP);    
    
    packet_type = 0;
    while (1) {
       if (packet_receive(buffer) == 0) {
       
            if ((ehdr->eth_ethernet_type == 0x0806) 
                    && ip_equal(apacket->arp_dst_IP, src_IP)
                    && ip_equal(apacket->arp_src_IP, dst_IP)
                    && (apacket->arp_operation == 2))
            {
                memcpy(dst_MAC, apacket->arp_src_MAC, 6);
                break;
            }
            
        }
    }

    puts("ROUTER ");
    for (i = 0; i < 5; i++) {
        putxval(dst_MAC[i], 2);
        puts(":");
    }
    putxval(dst_MAC[i], 2);
    puts("\n");

    // ARP & PING応答
    while (1) {
        if (packet_receive(buffer) == 0) {

            if ((ehdr->eth_ethernet_type == 0x0806) 
                    && ip_equal(apacket->arp_dst_IP, src_IP)
                    && (apacket->arp_operation == 1))
            {

                puts("a");
                arp_reply(buffer);            
                
            } else if ((ehdr->eth_ethernet_type == 0x0800) 
                    && ip_equal(ihdr->ip_dst_IP, src_IP)
                    && (ihdr->ip_protocol == 1)
                    && (phdr->ping_type == 8))
            {

                puts("p");
                ping_reply(buffer);

            }
        
        }
    }
    
    kz_kmfree(buffer);

    return 0;
}
